//: [Previous](@previous)
//:## 内存管理
  /*
   跟OC一样, Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)
   
   Swift的ARC种有3种引用
   1. 强引用(strong reference): 默认情况下, 引用都是强引用
   2. 弱引用(weak reference): 通过weak定义弱引用
    - 必须是可选类型的var, 因为实例销毁后, ARC会自动将弱引用设置为nil
    - ARC自动给弱引用设置为nil时, 不会触发属性观察器
   3. 无主引用(unowned reference): 通过unowned定义无主引用
    - 不会产生强引用, 实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe-unretained)
    - 试图在实例销毁后访问无主引用, 会产生运行时错误(野指针)
   */
//:## weak / unowned 的使用限制
/*
 weak / unowned 只能使用在类实例上面(ARC是针对堆空间, 只有类是放堆空间)
 */
protocol Livable : AnyObject {}
class Person {}

weak var p1: Person?
weak var p2: AnyObject? //AnyObject:代表任意类的实例,所以也是类
weak var p3: Livable? //Livable是遵守AnyObject协议的( : AnyObject)就代表着这个协议将来只能被类遵守

unowned var p11: Person?
unowned var p22: AnyObject?
unowned var p33: Livable?
//: ## AutoReleasepool(自动释放池)
/*
 autoreleasepool 在 Swift 中主要用于与 Objective-C 代码交互时管理临时对象的内存。通过合理使用 autoreleasepool，可以有效减少内存峰值，提高程序的性能。
 举例:
 func generateLotsOfTemporaryObjects() {
     for _ in 0..<10000{
         let tempObj = NSString(string: "temp")
     }//这一操作模拟OC交互时产生了大量的临时对象
 }
 func main() {
     generateLotsOfTemporaryObjects {
         generate()
     }
 }//在这个例子中，generateLotsOfTemporaryObjects 函数会生成 10000 个 NSString 对象。如果没有 autoreleasepool，这些对象会在函数结束时才被释放，可能会导致内存峰值。通过使用 autoreleasepool，这些对象在每次循环结束后都会被及时释放，从而减少内存占用。
 */
//: ## 循环引用(reference cycle)
/*
 weak / unowned都能解决循环引用问题, unowned 要比 weak 少一些性能消耗
  - 在生命周期中可能会变为nil的 使用weak
  - 初始化赋值后再也不会变为nil的 使用unowned
 */
//: ## 闭包的循环引用
/*
 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
 
 */
class Person3 {
    var fn: ( ()->() )?
    func run() {print("func run")}
    deinit {
        print("deinit")
    }
}
func test() {
    let p = Person3()
    p.fn = {
        p.run()
    }
}
test() //这样子操作会产生循环引用, 因为p的闭包引用了外层对象,导致Person3对象无法释放(没有调用deinit)
print("------111")
//在闭包表达式的捕获列表声明weak 或 unowned引用, 可以解决循环引用问题
func test1() {
    let p = Person3()
    p.fn = {
        [weak p] in //[weak p]是weak表达, in是用于隔开代码
        p?.run() //因为weak是可选项,所以要加?
    }
}
test1()
/*
 也可以用unowned
 p.fn = {
     [unowned p] in
     p.run() //unowned不是也选项,所以直接使用
 }
 
 当然还可以重新命名,有些时候这样子写在开发中对后面的代码更容易看懂
 p.fn = {
     [weak wp = p, unonwed up = p, a = 10+20] in
     p?.run()
 }
 a = 10+20是想说明甚至可以在捕获列表里面写其他内容
 */  
//:## 闭包的循环引用

/*
 如果想在定义闭包属性的同时引用self, 这个闭包必须是lazy的(因为在实例初始化完毕之后才能使用self)
 */
class per1 {
//    var fn:( ()->() ) = {
//        self.run()
//    }直接这样子写是报错的, 实例在没有全部实例完成初始化之前, 是不能使用self的, 所以必须加上lazy, 确保在初始化的时候这个实例不需要
    
    lazy var fn:( ()->() ) = {
//        self.run() //这样子写是可以运行了,但是会互相引用,导致无法销毁,所以要加上弱引用
        [weak p = self] in
        p?.run()
    }
    func run() {print("run")}
    deinit { print("deinit")}
}
print("----2")
func test2() {
    var per2 = per1()
    per2.fn
}
test2()
/*
 如果lazy属性是闭包调用的结果, 那么就不用考虑循环引用的问题(因为闭包调用后,闭包的生命周期就结束了)
 class per1 {
    var age = 10
     lazy var getAge: Int = {
        self.age
    }() 这里的()表示这个闭包表达式是直接调用的,其实就是相当于这个闭包表达式=self.age
 其实内存格式就是lazy var getAge: Int = self.age, 所以闭包表达式运行完之后就销毁了, 就不会存在循环引用
     deinit { print("deinit")}
 }
 */
//: ## @escaping
/*
 非逃逸闭包/逃逸闭包, 一般都是当做参数传递给函数
 - 非逃逸闭包: 闭包调用发生在函数结束前, 闭包调用在函数作用域内
 - 逃逸闭包: 闭包有可能在函数结束后调用, 闭包调用逃离了函数的作用域, 需要通过 @escaping声明
 */
import Dispatch
typealias Fn = ()->()

// fn是非逃逸闭包
func test11(_ fn: Fn) { fn() }
// fn是逃逸闭包
var gFn: Fn?
func test22(_ fn: @escaping Fn) { gFn = fn}//这种就是闭包其实是在调用gFn的时候才调用的,和函数test22没关系了
// fn是逃逸闭包
func test33(_ fn: @escaping Fn) {
    DispatchQueue.global().async {//异步调用,因为是异步调用,有可能函数test33已经调用完了,fn还没调用,所以也是逃逸闭包
        fn()
    }
}
//:## 内存访问冲突 (conflicting access to memory)
/*
 内存访问冲突会在两个访问满足下列条件时发生
  1. 至少一个是写入操作
  2. 他们访问的是同一块内存
  3. 他们的访问时间重叠(比如在同一个函数内)
 */

/*
 不存在内存冲突, plus(&number)相当于 number+1, 并没有同时操作自己
 */
func plus(_ num: inout Int) -> Int { num+1 }
var number = 1
number = plus(&number)

/*
 存在内存访问冲突,报👇🏻
 Simultaneous accesses to 0x102bd4928, but modification requires exclusive access.
 同时访问0x102bd4928，但修改需要独占访问。
 increment(&step) 相当于 { step += step }, step自己要同时做写入和访问, 同一个作用域内还是同时间访问
 */
var step = 1
func increment(_ num: inout Int) { num += step }
//increment(&step)

/*
 接近内存访问冲突, 使用临时值替换, 用替换值去访问内存, 最后赋值回来原值
 */
var copyOfStep = step
increment(&copyOfStep)
step = copyOfStep

/*
 如果下面的条件可以满足, 就说明重叠访问结构体的属性是安全的
  1. 你只访问实例存储属性, 不是计算属性或类属性(计算属性本质是方法,所以你不知道方法里面还有没有别的属性. 类属性存放在堆空间所以不行)
  2. 结构体是局部变量而非全局变量
  3. 结构体要么没有被闭包捕获 要么只被非逃逸闭包捕获
 */

/*
 👇🏻代码的实际原理
 class class11{}
 var var11 = class11()

 1.系统分配了堆空间的内存给到class11对象
 2.把堆空间class11对象的这个内存地址 赋值给了var11对象, 所以var11存放的是一个堆空间的地址, 也可以理解为是一个引用类型, 本质也是一个指针变量
 */

//: ## 指针
/*
 Swift中也有专门的指针类型, 这些都被定性为 Unsafe(不安全的), 常见的有以下4种类型
 - unsafePointe<Pointee> 类似与 const Pointee *
   const: 只能访问帮你修改 Pointee:泛型
 - unsafeMutablePointe<Pointee> 类似与 Pointee *
 Mutable: 可以修改
 - unsafeRawPointer 类似与 const void*
   这种不支持泛型, 其实就是个普通的不可以修改的指针, 比如:指向的数据是Int还是Double呢,这些就不确定了
 - unsafeMutableRawPointer 类似与 const void*
   这种不支持泛型, 其实就是个普通的可以修改的指针
 */
var age = 10
func test1(_ ptr: UnsafeMutablePointer<Int>) {
    ptr.pointee = 20 //.pointee: 取出它所指向的数据并且是Int类型,其实就是直接操作内存内容
    print("test1",ptr.pointee)
}

func test2(_ ptr: UnsafePointer<Int>) {
    print("test2",ptr.pointee)
}

func test3(_ num: inout Int) {
    
}
print("---指针---")
test2(&age) //10
test1(&age) //20
test3(&age) //这里说明了以后看到(&age)是有两种可能,一种是指针类型Unsafe,一种是inout类型

age = 50
func test4(_ ptr: UnsafeRawPointer) { // const void*
    print("test4",ptr.load(as: Int.self))//.load(as: Int.self): 加载什么类型的字节,告知内存需要多大(UnsafeRawPointer的取值)
}
test4(&age) //50

func test5(_ ptr: UnsafeMutableRawPointer) { // void*
    ptr.storeBytes(of: 30, as: Int.self) //store:存入的意思, 就是给多少位字节,并且我存入了什么(UnsafeMutableRawPointer的赋值方法)
}
test5(&age)
print(age)//30
//: ## 获得某个变量的指针
var a = 11
var ptr1 = withUnsafeMutablePointer(to: &a) {$0}
var ptr2 = withUnsafePointer(to: &a) {$0}
print(ptr2.pointee) //ptr2是指向a的指针,a现在的值是11,所以ptr2.pointee=11
ptr1.pointee = 22 //通过指针,直接把a的内容改成22
print(a) //22
//需求: 假如需要把ptr1在初始化的时候用UnsafeRawPointer类型接收,👇🏻做法,在$0那里入手,这个闭包是指给什么类型最后面就是返回什么类型
var ptr3 = withUnsafeMutablePointer(to: &a) { UnsafeRawPointer($0) }
//: ## 获得指向堆空间实例的指针
class duiClass {}
var duiVar1 = duiClass()
var ptrD = withUnsafePointer(to: &duiVar1) { UnsafeRawPointer($0)}
var heapPtrD = UnsafeRawPointer(bitPattern: ptrD.load(as: UInt.self))//bitPattern传入一个内存地址
print(heapPtrD!)
//: ## 创建指针
import Foundation
var ptrZhi = UnsafeRawPointer(bitPattern:  0x10001234)
//创建
var ptr11 = malloc(16)
//存
ptr11?.storeBytes(of: 11, as: Int.self) //存入11到前8位(int是占8位)
ptr11?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)//存入22到第8位开始数起的8位字节(int是占8位)
//取
print((ptr11?.load(as: Int.self))!) //11
print((ptr11?.load(fromByteOffset: 8, as: Int.self))!)//22
//销毁
free(ptr11)














//: [Next](@next)
